﻿using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.StaticFiles;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Xman.Extensions.Core;
using Xman.Extensions.Http;

namespace Xman.Dashboard.Logger.Sqlite
{
    public class LogMiddleware
    {
        private const string EmbeddedFileNamespace = "Xman.Dashboard.Logger.log_ui_dist";

        private readonly LogOptions _options;
        private readonly StaticFileMiddleware _staticFileMiddleware;


        public LogMiddleware(RequestDelegate next, IWebHostEnvironment hostingEnv, ILoggerFactory loggerFactory, LogOptions options)
        {
            _options = (options ?? new LogOptions());
            _staticFileMiddleware = CreateStaticFileMiddleware(next, hostingEnv, loggerFactory, options);
        }

        public async Task Invoke(HttpContext httpContext)
        {
            var httpMethod = httpContext.Request.Method;
            var path = httpContext.Request.Path.Value;

            // 显示Log界面
            if (httpMethod == "GET" && Regex.IsMatch(path, $"^/?{Regex.Escape(_options.RoutePrefix)}/?$", RegexOptions.IgnoreCase))
            {
                // Use relative redirect to support proxy environments
                var relativeIndexUrl = string.IsNullOrEmpty(path) || path.EndsWith("/")
                    ? "index.html"
                    : $"{path.Split('/').Last()}/index.html";

                RespondWithRedirect(httpContext.Response, relativeIndexUrl);
                return;
            }
            if (httpMethod == "GET" && Regex.IsMatch(path, $"^/{Regex.Escape(_options.RoutePrefix)}/?index.html$", RegexOptions.IgnoreCase))
            {
                await RespondWithIndexHtml(httpContext.Response);
                return;
            }
            //查询日志操作
            if (httpMethod == "POST" && Regex.IsMatch(path, $"^/?{Regex.Escape($"{_options.RoutePrefix}/querylog")}/?$", RegexOptions.IgnoreCase))
            {
                try
                {
                    await RespondWithJson(httpContext);
                }
                catch (Exception ex)
                {

                }
                return;
            }
            await _staticFileMiddleware.Invoke(httpContext);
        }

        private StaticFileMiddleware CreateStaticFileMiddleware(RequestDelegate next, IWebHostEnvironment hostingEnv, ILoggerFactory loggerFactory, LogOptions options)
        {
            var staticFileOptions = new StaticFileOptions
            {
                RequestPath = string.IsNullOrEmpty(options.RoutePrefix) ? string.Empty : $"/{options.RoutePrefix}/log_ui_dist",
                FileProvider = new EmbeddedFileProvider(typeof(LogMiddleware).GetTypeInfo().Assembly, EmbeddedFileNamespace),
            };

            return new StaticFileMiddleware(next, hostingEnv, Options.Create(staticFileOptions), loggerFactory);
        }

        private void RespondWithRedirect(HttpResponse response, string location)
        {
            response.StatusCode = 301;
            response.Headers["Location"] = location;
        }

        private async Task RespondWithIndexHtml(HttpResponse response)
        {
            response.StatusCode = 200;
            response.ContentType = "text/html;charset=utf-8";

            await using var stream = _options.IndexStream();
            // Inject arguments before writing to response
            var htmlBuilder = new StringBuilder(await new StreamReader(stream).ReadToEndAsync());
            //htmlBuilder.Replace(entry.Key, entry.Value);
            await response.WriteAsync(htmlBuilder.ToString(), Encoding.UTF8);
        }

        private async Task RespondWithJson(HttpContext httpContext)
        {
            httpContext.Response.StatusCode = 200;
            httpContext.Response.ContentType = "application/json";

            var input = await httpContext.Request.RequestTo<LogModelInput>();

            await using FileStream fs = new(input.FilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
            using var sr = new StreamReader(fs, Encoding.Default);
            var text = await sr.ReadToEndAsync();
            if (string.IsNullOrEmpty(text))
            {
                return;
            }
            var sections = text.Split(_options.EndMark).ToList();
            if (!sections.Any())
            {
                return;
            }
            if (!string.IsNullOrEmpty(input.Keywords))
            {
                sections = sections.Where(t => t.Contains(input.Keywords)).ToList();
            }
            var pageData = new PageData<LogDto>
            {
                PageSize = input.PageSize,
                PageIndex = input.PageIndex,
            }; 
            var logdtos = new List<LogDto>();
            for (var i = 0; i < sections.Count; i++)
            {
                var nodes = sections[i].Split(_options.SplitMark).ToList();
                if (!nodes.Any() || nodes.Count < 5)
                {
                    continue;
                }
                var logdto = new LogDto
                {
                    Index = i + 1,
                    WriteTime = nodes[0],
                    Level = nodes[1],
                    ActivityId = nodes[2],
                    CallSite = nodes[3],
                    Message = $"<xmp>{nodes[4]}</xmp>"
                };
                logdtos.Add(logdto);
            }
            pageData.PageTotal = logdtos.Count;
            pageData.ResultT = (input.PageIndex != null && input.PageSize != null) ? logdtos.Skip((input.PageIndex.To<int>() - 1) * input.PageSize.To<int>()).Take(input.PageSize.To<int>()).ToList() : logdtos;

            var json = JsonConvert.SerializeObject(pageData);
            await httpContext.Response.WriteAsync(json, Encoding.UTF8);
        }
    }
}
